package com.dianwoba.core.solr;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;

import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrQuery.ORDER;
import org.apache.solr.client.solrj.SolrQuery.SortClause;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.response.FacetField;
import org.apache.solr.client.solrj.response.FacetField.Count;
import org.apache.solr.client.solrj.response.QueryResponse;

import com.dianwoba.core.exception.SystemException;
import com.dianwoba.core.solr.where.SolrWhere;

/**
 * SOLR条件设定选择器
 * @author 堵成杰
 * @since 2.0
 * @param <T>
 */
public class SolrSelect<T> {

	/**
	 * 检索对象类
	 */
	protected Class<T> baseClass;
	
	/**
	 * 检索ID
	 */
	protected Object id;

	/**
	 * 检索结果数量
	 */
	protected int limit;
	
	/**
	 * fdacet检索结果数量
	 */
	protected int facetLimit;
	
	/**
	 * fdacet最小计数
	 */
	protected int facetMinCount;
	/**
	 * 检索开始点
	 */
	protected int offset = 0;

	/**
	 * 取得总结果数量
	 */
	protected long totalHits = 0;

	
	protected boolean spatialSearch;
	
	/**
	 * 纬度
	 */
	protected double lat;
	
	/**
	 * 经度
	 */
	protected double lon;
	
	/**
	 * 距离
	 */
	protected float distance = 10;
	
	/**
	 * 坐标字段名
	 */
	protected  String sfield = "position";
	
	/**
	 * 多边形字段名
	 */
	protected  String pfield = "scope4";
	
	/**
	 * 忽略多边形查询
	 */
	protected boolean ignoreScope = false;
	
	/**
	 * 忽略位置排序
	 */
	protected boolean ignoreScore = false;
	
	/**
	 * 是否距离过滤
	 */
	protected  boolean filter = false;
	
	/**
	 * where对象
	 */
	protected SolrWhere where;
	
	/** 
	 * 过滤where对象
	 */ 
	protected SolrWhere filterWhere;
	
	/**
	 * 排序字符串列表
	 */
	protected List<String> sortColumns = new ArrayList<String>();
	
	protected List<String> facetColumns = new ArrayList<String>();
	
	/**
	 * 匹配度
	 */
	protected static final String SCORE = "score";
	
	/**
	 * 查询结果
	 */
	protected List<T> resultList = null;
	
	/**
	 * SolrService
	 */
	protected SolrService solrService;

	List<String> fields = new ArrayList<String>();;
	
	List<FacetField> facetFieldList = null;
	
	List<String> hlColumns = new ArrayList<String>();
	
	Map<String,Map<String,List<String>>> highLighting = null;
	
	/**
	 * 构造
	 * @param solrService
	 * @param baseClass
	 */
	public SolrSelect(SolrService solrService, Class<T> baseClass) {
		this.solrService = solrService;
		this.baseClass = baseClass;
		this.limit = solrService.getDefaultLimit();
	}
	

	/**
	 * 构造
	 * @param solrService
	 * @param baseClass
	 */
	public SolrSelect(SolrService solrService) {
		this.solrService = solrService;
		this.limit = solrService.getDefaultLimit();
	}
	
	/**
	 * 取得查询结果
	 * @return
	 */
	public List<T> getResultList() {

		//执行查询操作
		execute();
		
		//处理查询结果
		return processResult();
	}
	
	/**
	 * 
	 * @description
	 * 	总数
	 * @author zhaoql
	 * @creationDate. 2014年6月24日 下午7:01:41 
	 * @return
	 */
	public long getNumFound(){
		
		//执行查询操作
		executeCount();
		
		return totalHits;
	}
	
	/**
	 * 取得单条查询结果
	 * @return
	 */
	public T getSingleResult(){
		
		//执行查询
		execute();
		if(resultList == null || resultList.size() == 0){
			return null;
		}
		return resultList.get(0);
	}
	
	/**
	 * 执行SOLR查询操作
	 */
	protected void execute(){
		try {
			
			//取得SLOR服务器对象
			SolrServer server = solrService.getSolrServer();
			
			//设定SOLR查询对象
			SolrQuery query = createQuery();
			System.out.println("query:"+query);
			//执行查询操作
		    QueryResponse rsp = server.query( query );
		    
		    //设定总结果数量
		    totalHits = rsp.getResults().getNumFound();;
		    
		    //设定查询结果
			resultList = rsp.getBeans(baseClass);
			
			//面查询结果
			facetFieldList = rsp.getFacetFields();
			
			//高亮结果
			highLighting = rsp.getHighlighting();
			
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new SystemException("[ERROR]SLOR进行查询时发生错误。", ex);
		}
	}
	
	/**
	 * 执行SOLR查询操作
	 * zql
	 */
	protected void executeCount(){
		
		try {
			
			//取得SLOR服务器对象
			SolrServer server = solrService.getSolrServer();
			
			//设定SOLR查询对象
			SolrQuery query = createQuery();
			System.out.println("query:"+query);
			//执行查询操作
		    QueryResponse rsp = server.query( query );
		    
		    //设定总结果数量
		    totalHits = rsp.getResults().getNumFound();;
			
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new SystemException("[ERROR]SLOR进行查询时发生错误。", ex);
		}
	}
	
	/**
	 * 处理查询结果
	 * @return
	 */
	private List<T> processResult(){

		SolrResultList<T> list = new SolrResultList<T>();
		for(T entity : resultList){
			
			if(entity instanceof SolrDTO){
				
				SolrDTO solrDto = (SolrDTO)entity;
				for(String hlColumn : hlColumns){
					
					if(highLighting.get(solrDto.getId())!=null&&highLighting.get(solrDto.getId()).get(hlColumn)!=null){
						
						try {
							SolrDTO.class.getDeclaredField(hlColumn).
									set(solrDto, highLighting.get(solrDto.getId()).get(hlColumn).get(0));
						} catch (Exception e) {
							e.printStackTrace();
							continue;
						} 
					}
				}
			}
			
			list.add(entity);
		}
		
		if(isFacetFieldSearch()){
			Map<String,Map<String,Long>> facetMap = new HashMap<String,Map<String,Long>>();
			for(FacetField field : facetFieldList){
				
				String fieldName = field.getName();
				List<Count> countList = field.getValues();
				if(countList == null) {
					continue;
				}
				Map<String,Long> countMap = new HashMap<String,Long>();
				for(Count count : countList){
					countMap.put(count.getName(), count.getCount());
				}
				facetMap.put(fieldName, countMap);
			}
			list.setFacetMap(facetMap);
		}

		list.setTotalHits(totalHits);
		
		return list;
	}
	
	/**
	 * 创建SOLR查询对象
	 * @return
	 */
	protected SolrQuery createQuery() {
		SolrQuery query = new SolrQuery();
		
		//设定fl条件
		setFields(query);
		
		//设定where条件
		setWhere(query);

		//设定过滤where条件
		setFilterWhere(query);
		
		//设定排序条件
		setSortField(query);
		
		//设定分页条件
		setLimitOffset(query);
		
		//设定面查询条件
		setFacetField(query);
		
		//设定高亮显示条件
		setHighLightField(query);
				
		return query;
	}
	
	/** 
	 * @Description: 设定过滤WHERE条件
	 *
	 * @author Liliangjie  
	 * @date 2014年11月19日 下午3:51:36 
	 *
	 * @param query 
	 */
	protected void setFilterWhere(SolrQuery query) {
		query = query.setFilterQueries(convertWhere(filterWhere)+(ignoreScope?"":setSpatial()));
	}


	/**
	 * 设定fl条件
	 * @param query
	 */
	protected void setFields(SolrQuery query){
		
		if (getFields()!= null && getFields().size() > 0) {
			for (int i = 0; i < getFields().size(); i++) {
				query.addField(getFields().get(i));
			}
		}
	}
	
	/**
	 * 设定WHERE条件
	 * @param query
	 */
	protected void setWhere(SolrQuery query) {
		if(id != null){
			query.setQuery("id:" + id.toString());
		} else {
			query = query.setQuery(convertWhere(where)+(spatialSearch?setSpatialSort(query):""));
		}
	}
	
	/**
	 * 设定排序条件
	 * @param query
	 */
	protected void setSortField(SolrQuery query) {
		if (!sortColumns.isEmpty()) {
			for (String sortColumn : sortColumns) {

				String sortField = SolrUtils.getSortFieldName(sortColumn);
				ORDER order = SolrUtils.getOrder(sortColumn);

				if(sortField.equals(SCORE)){
					query.addSort(new SortClause(SCORE, ORDER.desc));
					break;
				}
				query.addSort(new SortClause(sortField, order));
			}
		}

	}
	
	/**
	 * 设定空间位置排序权重
	 * @param query
	 * @return
	 */
	protected String setSpatialSort(SolrQuery query){
		
		StringBuffer buffer = new StringBuffer();
		if(where != null) {
			buffer.append(" AND ");
		}
		buffer.append("{!geofilt score=distance");
		buffer.append(" sfield=");
		buffer.append(this.sfield);
		if(this.distance == 0) {
			buffer.append(" filter=");
			buffer.append(this.filter);
		}
		buffer.append(" pt=");
		buffer.append(String.valueOf(this.lat));
		buffer.append(",");
		buffer.append(String.valueOf(this.lon));
		buffer.append(" d=");
		buffer.append(this.distance);
		buffer.append("}");
		/*buffer.append(" and ");
		buffer.append(pfield);
		buffer.append(":\"Intersects(");
		buffer.append(String.valueOf(this.lon));
		buffer.append(" ");
		buffer.append(String.valueOf(this.lat));
		buffer.append(")\" ");*/
		
		if(!ignoreScore){
			query.addSort(new SortClause("score", ORDER.asc));
		}
		System.out.println("=====================>"+buffer.toString());
		return buffer.toString();
		
	}
	
	/**
	 * 设定空间位置搜索条件
	 * @author liulj
	 * @date 2014年11月20日 下午7:44:02
	 * @return
	 */
	protected String setSpatial(){
		StringBuffer buffer = new StringBuffer();
		if(this.lon != 0.0 || this.lat != 0.0){
			buffer.append(" AND ");
			buffer.append(pfield);
			buffer.append(":\"Intersects(");
			buffer.append(String.valueOf(this.lon));
			buffer.append(" ");
			buffer.append(String.valueOf(this.lat));
			buffer.append(")\" ");
		}
		
		return buffer.toString();
	}

	protected void setHighLightField(SolrQuery query) {
		if (!hlColumns.isEmpty()) {
			for (String hlColumn : hlColumns) {

				query.addHighlightField(hlColumn);
			}
		}
	}
	
	/**
	 * 设定分页信息条件
	 * @param query
	 */
	protected void setLimitOffset(SolrQuery query) {
		query.setStart(offset);
		query.setRows(limit);
	}
	
	
	/**
	 * where条件->字符串转换
	 * @param where	where对象
	 * @return		查询字符串
	 */
	public String convertWhere(SolrWhere where){
		if(where == null) return "";
		
		String queryStr = where.getCriteria().
							replace(SolrConditionType.TABLE_REPLACE_WORD, "");
		StringBuffer q = new StringBuffer();
		Matcher fm = SolrConditionType.FIELD_REPLACE_PATTERN.matcher(queryStr);
		
		while (fm.find()) {
			String propertyName = fm.group(1);
			fm.appendReplacement(q, propertyName);
		}
		fm.appendTail(q);
		return q.toString();
	}
	
	
	/**
	 * 指定where条件对象
	 * @param whereVal	where值
	 * @return
	 */
	public SolrSelect<T> where(SolrWhere whereVal) {
		this.where = whereVal;

		return this;
	}

	/**
	 * 指定where条件对象
	 * @param whereVal	where值序列
	 * @return
	 */
	public SolrSelect<T> where(SolrWhere... wheres){

		this.where = SolrUtils.complexSingleWhere(wheres);

		return this;
	}
	
	/**
	 * 指定过滤where条件对象
	 * @param whereVal	where值
	 * @return
	 */
	public SolrSelect<T> filterWhere(SolrWhere whereVal) {
		this.filterWhere = whereVal;

		return this;
	}
	
	/**
	 * 指定排序条件对象
	 * @param orderBy
	 * @return
	 */
	public SolrSelect<T> orderBy(CharSequence orderBy) {
		if(!orderBy.equals("")){
			String[] orderColumns = orderBy.toString().split(",");
			for (int i = 0; i < orderColumns.length; i++) {
				sortColumns.add(orderColumns[i]);
			}
		}
		return this;
	}
	
	/**
	 * 空间位置查询2.0
	 * @param lat		纬度
	 * @param lon		经度
	 * @param radius	半径
	 * @return
	 */
	public SolrSelect<T> spatial(double lat ,double lon, int scope){
		
		this.lat = lat;
		this.lon = lon;
		if(scope==0){
			this.distance = 0;	
			this.ignoreScope = true;
		}else{
			this.pfield = "scope"+scope;
			this.ignoreScope = false;
		}
		
		this.spatialSearch = true;
		return this;
		
	}
	public SolrSelect<T> spatial(double lat ,double lon, int scope, boolean ignoreScore){
		
		this.lat = lat;
		this.lon = lon;
		if(scope==0){
			this.distance = 100;	
			this.ignoreScope = true;
		}else{
			this.pfield = "scope"+scope;
			this.ignoreScope = false;
		}
		
		this.ignoreScore = ignoreScore;
		
		this.spatialSearch = true;
		return this;
		
	}
	
	
	
	
	/**
	 * 空间位置排序
	 * @param lat		纬度
	 * @param lon		经度
	 * @param radius	半径
	 * @return
	 */
	public SolrSelect<T> spatialSort_(double lat ,double lon){
		
		this.lat = lat;
		this.lon = lon;
		this.filter = false;
		this.sfield = "position";
		this.spatialSearch = true;
		return this;
		
	}
	/**
	 * 指定排序条件对象
	 * @param orderBy
	 * @return
	 */
	public SolrSelect<T> hightLight(CharSequence hl) {
		String[] hls = hl.toString().split(",");
		for (int i = 0; i < hls.length; i++) {
			hlColumns.add(hls[i]);
		}
		return this;
	}
	
	/**
	 * 指定fl对象
	 * @param orderBy
	 * @return
	 */
	public SolrSelect<T> fields(CharSequence fl) {
		String[] fls = fl.toString().split(",");
		for (int i = 0; i < fls.length; i++) {
			fields.add(fls[i]);
		}
		return this;
	}
	
	/**
	 * 指定id
	 * @param id
	 * @return
	 */
	public SolrSelect<T> id(Object id) {
		this.id = id;
		return this;
	}
	
	public SolrSelect<T> groupBy(CharSequence groupBy) {
		String[] groupColumns = groupBy.toString().split(",");
		for (int i = 0; i < groupColumns.length; i++) {
			facetColumns.add(groupColumns[i]);
		}
		return this;
	}
	
	public SolrSelect<T> facetLimit(int limitVal) {
		this.facetLimit = limitVal;
		return this;
	}
	
	public SolrSelect<T> facetMinCount(int countVal) {
		this.facetMinCount = countVal;
		return this;
	}
	
	protected void setFacetField(SolrQuery query) {
		if (!facetColumns.isEmpty()) {
			for (String facetColumnCtrl : facetColumns) {
				String facetColumn = facetColumnCtrl;
				query.addFacetField(facetColumn);

			}
		}
		
		if(facetLimit != 0){
			query.setFacetLimit(facetLimit);
		}
		if(facetMinCount != 0){
			query.setFacetMinCount(facetMinCount);
		}
	}

	protected boolean isFacetFieldSearch() {
		return facetColumns != null && facetColumns.size() > 0;
	}

	public SolrSelect<T> limit(int limitVal) {
		this.limit = limitVal;
		return this;
	}

	public SolrSelect<T> offset(int offsetVal) {
		this.offset = offsetVal;
		return this;
	}

	public List<String> getFields() {
		return fields;
	}

	public void setFields(List<String> fields) {
		this.fields = fields;
	}
	
}
